#include "game.h"
#include "ui_game.h"

Game::Game(QWidget *parent):
    QWidget(parent),
    ui(new Ui::Game)
{
    ui->setupUi(this);
    startMusicPlayer();
    InitGame();
    StartGame();
}

Game::~Game()
{
    delete ui;
}

//田字型方块
int item1[4][4]=
    {
        {0,1,1,0},
        {0,1,1,0},
        {0,0,0,0},
        {0,0,0,0}
};

//L型方块
int item2[4][4]=
    {
        {0,1,0,0},
        {0,1,0,0},
        {0,1,1,0},
        {0,0,0,0}
};

//反L型方块
int item3[4][4]=
    {
        {0,0,1,0},
        {0,0,1,0},
        {0,1,1,0},
        {0,0,0,0}
};

//闪电型方块
int item4[4][4]=
    {
        {0,1,0,0},
        {0,1,1,0},
        {0,0,1,0},
        {0,0,0,0}
};

//反闪电型方块
int item5[4][4]=
    {
        {0,0,1,0},
        {0,1,1,0},
        {0,1,0,0},
        {0,0,0,0}
};

//T字型方块
int item6[4][4]=
    {
        {1,1,1,0},
        {0,1,0,0},
        {0,0,0,0},
        {0,0,0,0}
};

//竖1型方块
int item7[4][4]=
    {
        {0,0,1,0},
        {0,0,1,0},
        {0,0,1,0},
        {0,0,1,0}
};
//将两个GameState的枚举值进行按位或运算，并返回结果。
inline GameState operator|(GameState a, GameState b)
{return static_cast<GameState>(static_cast<int>(a) | static_cast<int>(b));}


//将传入的sblock复制给传入的dblock
inline void block_cpy(int dblock[4][4],int sblock[4][4])
{
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            dblock[i][j]=sblock[i][j];
        }
    }
}

//测试
void Debug(Game *obj){
    int temp_block1[4][4];
    block_cpy(temp_block1, item7);
    Border temp_border1;
    obj->GetBorder(temp_block1,temp_border1);

}

//背景音乐，未传入音乐
void Game::startMusicPlayer(){
    QMediaPlayer* player = new QMediaPlayer;
    player->play();
}

//初始化游戏的各种参数和界面
void Game::InitGame()
{
    for(int i=0;i<AREA_ROW;i++){
        for(int j=0;j<AREA_COL;j++){
            game_area[i][j]=0;
        }
    }

    speed_ms=800;
    refresh_ms=3;
    srand(time(0));
    score=0;
    loadUI();
}

//开始游戏
void Game::StartGame()
{
    game_state = FreeRun;

    game_timer=startTimer(speed_ms);
    paint_timer=startTimer(refresh_ms);

    //生成一个随机数 block_id，用于确定下一个方块的类型
    int block_id=rand()%7;
    CreateBlock(next_block,block_id);
    ResetBlock();
    Debug(this);
}

//游戏结束，弹出QmessageBox消息，提示游戏结束
void Game::GameOver()
{
    game_state = Game_Over;

    killTimer(game_timer);
    killTimer(paint_timer);
    QMessageBox::information(this,"failed","game over, current score is:"+QString::number(score));
}

void Game::loadUI(){

}

//将下一个方块设置为当前方块，并从顶部进入游戏画面
void Game::ResetBlock()
{
    block_cpy(cur_block,next_block);
    GetBorder(cur_block,cur_border);

    int block_id=rand()%7;
    CreateBlock(next_block,block_id);

    block_point start_point;
    start_point.pos_x = AREA_COL / 2 - 2;
    start_point.pos_y = -4;
    block_pos=start_point;
}

//方块移动
void Game::BlockMove(Direction dir)
{
    switch (dir)//dir是传入的移动方向
    {
    case UP:
        //判断碰撞
        if(IsCollide(block_pos.pos_x,block_pos.pos_y,UP)){
            break;
        }
        //方块旋转
        BlockRotate(cur_block);
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if(block_pos.pos_y+i >=0 &&   game_area[block_pos.pos_y+i][block_pos.pos_x+j] != 2){
                    game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];
                }
            }
        }
        GetBorder(cur_block,cur_border);
        break;
    case DOWN:
        //如果方块触底，则固定到底部
        if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1)
        {
            game_state = Wait;

            ConvertStable(block_pos.pos_x,block_pos.pos_y);
            ResetBlock();

            game_state = FreeRun;

            break;
        }
        //如果方块碰到底部的其他方块，则固定在底部
        if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))
        {

            game_state = Wait;

            ConvertStable(block_pos.pos_x,block_pos.pos_y);
            ResetBlock();

            game_state = FreeRun;

            break;
        }
        //清除当前方块在游戏区域中上一步位置的痕迹
        for(int j=cur_border.lbound;j<=cur_border.rbound;j++){
            if(block_pos.pos_y >=0){
                game_area[block_pos.pos_y][block_pos.pos_x+j]=0;
            }
        }
        //更新当前方块的垂直位置
        block_pos.pos_y+=1;
        //将当前方块的状态更新到游戏区域中
        for(int i=0;i<4;i++){
            for(int j=cur_border.lbound;j<=cur_border.rbound;j++){
                if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2){
                    if(block_pos.pos_y+i >=0){
                        game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];
                    }
                }
            }
        }
        break;
    //向左移动当前方块的判断
    case LEFT:
        if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT))
            break;
        for(int i=cur_border.ubound;i<=cur_border.dbound;i++){
            if(block_pos.pos_y+i >=0){
                game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0;
            }
        }
        block_pos.pos_x-=1;
        for(int i=cur_border.ubound;i<=cur_border.dbound;i++){
            for(int j=0;j<4;j++){
                if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2){
                    if(block_pos.pos_y+i >=0){
                        game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];
                    }
                }
            }
        }
        break;
    //向右移动当前方块的判断
    case RIGHT:
        if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT))
            break;
        for(int i=cur_border.ubound;i<=cur_border.dbound;i++){
            if(block_pos.pos_y+i >=0){
                game_area[block_pos.pos_y+i][block_pos.pos_x]=0;
            }
        }
        block_pos.pos_x+=1;
        for(int i=cur_border.ubound;i<=cur_border.dbound;i++){
            for(int j=0;j<4;j++){
                if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2){
                    if(block_pos.pos_y+i >=0){
                        game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];
                    }
                }
            }
        }
        break;
        //直接落底判断
    case SPACE:
        game_state = Wait;

        //使用一个 while 循环，不断地向下移动当前方块，直到方块的下边界达到游戏区域的底部
        //这里的移动过程与向下移动时的操作类似，只是直到方块触底为止
        while(block_pos.pos_y+cur_border.dbound<AREA_ROW-1&&!IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))
        {
            for(int j=cur_border.lbound;j<=cur_border.rbound;j++){
                if(block_pos.pos_y >=0){
                    game_area[block_pos.pos_y][block_pos.pos_x+j]=0;
                }
            }
            block_pos.pos_y+=1;
            for(int i=0;i<4;i++){
                for(int j=cur_border.lbound;j<=cur_border.rbound;j++){
                    if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2){
                        if(block_pos.pos_y+i >=0){
                            game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];
                        }
                    }
                }
            }
        }
        ConvertStable(block_pos.pos_x,block_pos.pos_y);
        ResetBlock();
        game_state = FreeRun;

        break;
    default:
        break;
    }

    //检查游戏区域中是否有满行，并对满行进行消除
    int i=AREA_ROW-1;
    int line_count=0;
    while(i>=1)
    {
        bool is_line_full=true;
        for(int j=0;j<AREA_COL;j++)
            if(game_area[i][j]==0)
            {
                is_line_full=false;
                i--;
                break;
            }
        if(is_line_full)
        {
            for(int k=i;k>=1;k--){
                for(int j=0;j<AREA_COL;j++){
                    game_area[k][j]=game_area[k-1][j];
                }
            }
            //line_count为消除的行数
            line_count++;
        }
    }
    //每消除一行记10分
    score+=line_count*10;
    //判断图形界面第一行是否有固定的方块，有的话游戏结束
    for(int j=0;j<AREA_COL;j++)
        if(game_area[0][j]==2){
            GameOver();
        }
}
//方块旋转操作
void Game::BlockRotate(int block[4][4])
{
    int temp_block[4][4];

    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            temp_block[3-j][i]=block[i][j];
        }
    }

    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            block[i][j]=temp_block[i][j];
        }
    }
}
//每个item是提前写好的，一共七个，根据生成的随机种子从这里面选一个block
void Game::CreateBlock(int block[4][4],int block_id)
{
    switch (block_id)
    {
    case 0:
        block_cpy(block,item1);
        break;
    case 1:
        block_cpy(block,item2);
        break;
    case 2:
        block_cpy(block,item3);
        break;
    case 3:
        block_cpy(block,item4);
        break;
    case 4:
        block_cpy(block,item5);
        break;
    case 5:
        block_cpy(block,item6);
        break;
    case 6:
        block_cpy(block,item7);
        break;
    default:
        break;
    }
}

//计算边界
void Game::GetBorder(int block[4][4],Border &border)
{
    //计算上下左右的边界
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(block[i][j]==1){
                border.dbound=i;
                break;
            }
        }
    }

    for(int i=3;i>=0;i--){
        for(int j=0;j<4;j++){
            if(block[i][j]==1){
                border.ubound=i;
                break;
            }
        }
    }

    for(int j=0;j<4;j++){
        for(int i=0;i<4;i++){
            if(block[i][j]==1){
                border.rbound=j;
                break;
            }
        }
    }

    for(int j=3;j>=0;j--){
        for(int i=0;i<4;i++){
            if(block[i][j]==1){
                border.lbound=j;
                break;
            }
        }
    }
}

//当方块到达底部，将方块固定，此时不可进行移动操作
void Game::ConvertStable(int x,int y)
{
    for(int i=cur_border.ubound;i<=cur_border.dbound;i++){
        for(int j=cur_border.lbound;j<=cur_border.rbound;j++){
            if(y+i >= 0 && cur_block[i][j]==1){
                game_area[y+i][x+j]=2; //x,y颠倒
            }
        }
    }
}

//检测当前方块是否与游戏区域的其他方块或边界发生碰撞，以及是否超出了游戏区域的边界。
bool Game::IsCollide(int x,int y,Direction dir)
{
    int temp_block[4][4];
    block_cpy(temp_block,cur_block);
    Border temp_border;
    GetBorder(temp_block,temp_border);

    switch(dir)
    {
    case UP:
        BlockRotate(temp_block);
        GetBorder(temp_block,temp_border); //up（旋转）操作后，重新获取方块边界
        break;
    case DOWN:
        y+=1;
        break;
    case LEFT:
        x-=1;
        break;
    case RIGHT:
        x+=1;
        break;
    default:
        break;
    }

    for(int i=temp_border.ubound;i<=temp_border.dbound;i++){
        for(int j=temp_border.lbound;j<=temp_border.rbound;j++){
            if((game_area[y+i][x+j]==2&&temp_block[i][j]==1)
                ||x+temp_border.lbound<0
                ||x+temp_border.rbound>AREA_COL-1){
                return true;}
        }
    }

    return false;
}

//绘制玩游戏过程中的界面
void Game::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setBrush(QBrush(Qt::white,Qt::SolidPattern));

    //画背景
    painter.drawRect(MARGIN,MARGIN,AREA_COL*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE);

    //画下一个方块预览
    painter.setBrush(QBrush(Qt::yellow,Qt::SolidPattern));
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(next_block[i][j]==1){
                painter.drawRect(MARGIN*3+AREA_COL*BLOCK_SIZE+j*BLOCK_SIZE,MARGIN+i*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE);
            }
        }
    }

    QFontDatabase::addApplicationFont(":/src/font/pixel.ttf");

    //设置得分显示的颜色以及字体
    painter.setPen(Qt::white);
    painter.setFont(QFont(font_family, font_size));
    //显示分数
    painter.drawText(QRect(MARGIN*3+AREA_COL*BLOCK_SIZE,MARGIN*2+4*BLOCK_SIZE,BLOCK_SIZE*4,BLOCK_SIZE*4),
                     Qt::AlignCenter,
                     "SCORE: "+ QString::number(score));

    //画游戏过程中的方块，已经落入低端的为绿色，正在操作过程中的为红色
    for(int i=0;i<AREA_ROW;i++)
        for(int j=0;j<AREA_COL;j++)
        {
            if(game_area[i][j]==1)
            {
                painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern));
                painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);
            }
            else if(game_area[i][j]==2)
            {
                painter.setBrush(QBrush(Qt::red,Qt::SolidPattern));
                painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);
            }
        }
}

//如果游戏未结束，定时刷新游戏界面
void Game::timerEvent(QTimerEvent *event)
{
    if(event->timerId()==game_timer && game_state != Game_Over){
        BlockMove(DOWN);
    }
    if(event->timerId()==paint_timer){
        update();
    }
}

//将键盘操作传入游戏
void Game::keyPressEvent(QKeyEvent *event)
{
    if( game_state == FreeRun){

        switch(event->key())
        {
        case Qt::Key_Up:
            BlockMove(UP);
            break;
        case Qt::Key_Down:
            BlockMove(DOWN);
            break;
        case Qt::Key_Left:
            BlockMove(LEFT);
            break;
        case Qt::Key_Right:
            BlockMove(RIGHT);
            break;
        case Qt::Key_Space:
            BlockMove(SPACE);
            break;
        default:
            break;
        }

    }
}





